package com.nekonomics
package persistence.repository

import domain.entity.Payment
import types.*
import types.PaymentMethod.AliPay
import utils.SqlUtil
import utils.magic.SqlMagic

import doobie.*
import doobie.free.connection.ConnectionIO
import doobie.implicits.*
import doobie.postgres.implicits.*
import zio.{ULayer, ZLayer}

import java.time.OffsetDateTime

trait PaymentRepo {
  def insert(orderId: Long, totalAmount: Money, status: PaymentStatus, htmlForm: String, method: PaymentMethod = AliPay): ConnectionIO[Long]

  def findByOrderId(orderId: Long): ConnectionIO[Option[Payment]]

  def markAsPaid(orderId: Long, tradeNo: Option[String], paidAt: OffsetDateTime): ConnectionIO[Int] 
  
  def markAsClosed(orderId: Long): ConnectionIO[Int]  
}

object PaymentRepo {
  val live: ULayer[PaymentRepo] = ZLayer.succeed(new PaymentRepoLive)
}


class PaymentRepoLive extends PaymentRepo {
  override def insert(orderId: Long, totalAmount: Money, status: PaymentStatus, htmlForm: String, method: PaymentMethod = AliPay): ConnectionIO[Long] = {
    sql"""
      insert into public.payment (
        order_id, total_amount, status, method, html_form
      )
      values ($orderId, $totalAmount, $status, $method, $htmlForm)
      """
      .update
      .withUniqueGeneratedKeys("id")
  }

  override def findByOrderId(orderId: Long): ConnectionIO[Option[Payment]] = {
    SqlUtil.selectFromWhereOpt[Payment](Some(fr"order_id=$orderId"))
      .query[Payment]
      .option
  }


  override def markAsPaid(orderId: Long, tradeNo: Option[String], paidAt: OffsetDateTime): ConnectionIO[Int] = {
    sql"""
      update public.payment
      set trade_no = $tradeNo,
          paid_at = $paidAt,
          status = ${PaymentStatus.Success},
          updated_at = now()
      where order_id = $orderId
    """
      .update
      .run
  }


  override def markAsClosed(orderId: Long): ConnectionIO[Int] = {
    sql"""
         |update public.payment
         |set 
         |  status = ${PaymentStatus.Closed.toString},
         |  updated_at = now()
         |where order_id = $orderId
         |""".stripMargin
      .update
      .run
  }
}